package globulin;

import globulin.soundFXProccessing.SoundFX;
import jgame.*;
import jgame.platform.JGEngine.*;
import jgame.impl.JGEngineInterface.*;

/** This class defines an enemy in the game.
 * @author Sean Pavlichek & Jonathan Horvath
 */
public class Enemy extends JGObject {
    // Necessary variables

    private double timer = 0;
    private Globulin gameScreen;
    private int position;
    private int formation;
    private String[] type1 = new String[]{"enemy1_l3", "enemy1_l2", "enemy1_l1",
        "enemy1_c", "enemy1_r1", "enemy1_r2", "enemy1_r3"};
    private String[] type2 = new String[]{"enemy2_1", "enemy2_2"};
    private int type = 0;
    private Enemy leader;
    private Enemy follower;
    int level = 0;

    /**
     * Basic constructor.
     * @param parent The game screen. This variable is necessary for many functions.
     * @param inputType The type of enemy to be spawned
     */
    public Enemy(Globulin parent, int inputType) {
        // String name, boolean unique_id, double x, double y, int collisionID,
        // String gfxName, double xSpeed, double ySpeed
        super("enemy", true, 0, -8, Globulin.ENEMY_COLID, "enemy1_c", 0, 0);

        // Stores the game screen so that this class has access to its methods
        gameScreen = parent;

        if (inputType < 2 && inputType >= 0) {
            this.type = inputType;
        }

        if (type == 0) {
            this.setGraphic(type1[3]);
        } else if (type == 1) {
            this.setGraphic(type2[0]);
        }

        this.level = gameScreen.getLevel();

        this.setSpeed(gameScreen.random(-1.0, 1.0, .2), (1.0 + gameScreen.getLevel() / 2.0));
    }

    public Enemy(Globulin parent, Enemy other, int pos, int form, int inputType) {
        // String name, boolean unique_id, double x, double y, int collisionID,
        // String gfxName, double xSpeed, double ySpeed
        super("enemy", true, 0, -8, Globulin.ENEMY_COLID, "enemy1_c", 0, 0);

        // Stores the game screen so that this class has access to its methods
        gameScreen = parent;
        formation = form;
        level = gameScreen.getLevel();

        if (inputType < 2 && inputType >= 0) {
            this.type = inputType;
        }

        if (type == 0) {
            this.setGraphic(type1[3]);
        } else if (type == 1) {
            this.setGraphic(type2[0]);
        }

        if (pos > 1) {
            this.leader = other;
            this.position = pos;
        } else {
            this.leader = null;
            this.position = pos;
        }

        // Positions the ship and gives it speed
        if (position == 1) {
            this.setPos(gameScreen.random(100, 540, 2), -8);
        } else if (position == 2) {
            this.setPos(leader.x - 50, leader.y - 40);
        } else if (position == 3) {
            this.setPos(leader.x + 100, leader.y);
        } else if (position == 4 && form == 4) {
            this.setPos(leader.x - 50, leader.y - 40);
        } else if (position == 4 && form == 5) {
            this.setPos(leader.x - 50, leader.y - 10);
        } else if (position == 5) {
            this.setPos(leader.x, leader.y - 55);
        }

        if (this.leader != null) {
            this.setSpeed(leader.xspeed, leader.yspeed);
        } else {
            this.setSpeed(gameScreen.random(-1.0, 1.0, .2), (1.0 + level / 2.0));
        }
    }

    /**
     * Handles the move function for the ship. Also handles animation for the
     * ship based on the rate of movement to either side.
     */
    @Override
    public void move() {
        // Increments the timer
        timer += 1;

        if ((this.leader == null) && (timer % 20 == 0)) {
            // Changes the horizontal speed at certain intervals
            if (this.type == 0) {
                this.setSpeed(gameScreen.random(-1.0, 1.0, .2), this.yspeed);
            } else if (this.type == 1) {
                this.setSpeed(Math.cos(timer) + Math.sin(timer), (1.0 + level / 2.0) + this.xspeed);
            }
        } else if (timer % 20 == 0) {
            this.xspeed = leader.xspeed;
        }

        if (this.type == 0 && (timer % (200 - (2 * level)) == 0)) {
            if (level < 10 && (gameScreen.random(0, 20 - level, 1) < 5)) {
                new JGObject("en_missle", true, (x + (this.tilewidth / 2)),
                        (y + (this.tileheight / 2)), Globulin.ENEMY_MISSILE_COLID,
                        "en_missle", 0, this.yspeed + 2);
            } else if ((gameScreen.random(0, 10, 1)) < 4) {
                new JGObject("en_missle", true, (x + (this.tilewidth / 2)),
                        (y + (this.tileheight / 2)), Globulin.ENEMY_MISSILE_COLID,
                        "en_missle", 0, this.yspeed + 2);
            }
        }

        // Moves the ship to the left or right
        x += xspeed * 2;
        // Moves the ship up or down
        y += this.yspeed;

        // Handles the animation of the ship
        if (type == 0) {
            turnShip();
        } else if (type == 1 && (timer % 10 == 0)) {
            if (this.getGraphic().equals(type2[0])) {
                this.setGraphic(type2[1]);
            } else {
                this.setGraphic(type2[0]);
            }
        }

        // If the ship goes off of the bottom of the screen, it despawns
        if (y > gameScreen.pfHeight()) {
            if (follower != null && leader != null) {
                this.follower.leader = this.leader;
                leader.follower = this.follower;
                follower.positionChange();
            } else if (follower != null) {
                follower.leader = null;
                follower.positionChange();
            } else if (leader != null) {
                leader.follower = null;
            }
            this.setGraphic("shipExplosion");
            int timer = 0;
            while (timer < 30) {
                timer++;
            }
            this.remove();
        }

        // If the ship hits a wall, it will reverse direction
        if (x < 0 || x > (gameScreen.pfWidth() - this.tilewidth)) {
            if (this.leader == null) {
                if (follower == null) {
                    this.setSpeed(-(this.xspeed), this.yspeed);
                } else {
                    this.setSpeed(-(this.xspeed), this.yspeed);
                    follower.leaderChangingDir();
                }
            } else {
                if (follower == null) {
                    this.setSpeed(-(this.xspeed), this.yspeed);
                    leader.followerChangingDir();
                } else {
                    this.setSpeed(-(this.xspeed), this.yspeed);
                    leader.followerChangingDir();
                    follower.leaderChangingDir();
                }
            }
        }
    }

    /** Handles the collision of this object with another.
     * @param o The object colliding with the enemy.
     */
    @Override
    public void hit(JGObject o) {
        if (o.colid == Globulin.PLAYER_COLID || o.colid == Globulin.PLAYER_LASER_COLID) {
            if (follower != null && leader != null) {
                this.follower.leader = this.leader;
                leader.follower = this.follower;
                follower.positionChange();
            } else if (follower != null) {
                follower.leader = null;
                follower.positionChange();
            } else if (leader != null) {
                leader.follower = null;
            }
            this.remove();

            // Remove the weapon from the screen
            if (o.colid == Globulin.PLAYER_LASER_COLID) {
                o.remove();
            }

            gameScreen.score += 5;
            gameScreen.checkScoreForNewLife();

            // Show an explosion
            new JGObject("explosion", true, x, y, 0, "explosion", 0, 0, 32);

            // Play a sound effect
            gameScreen.soundFxThreadPool.execute(new SoundFX(gameScreen, "boomfx"));
        }
    }

    /** Handles the animation of the ship. The ship's picture changes based on
     * its horizontal velocity.
     */
    private void turnShip() {
        int newShipPic = (int) (this.xspeed / .3);
        switch (newShipPic) {
            case -3:
                this.setGraphic(type1[0]);
                break;
            case -2:
                this.setGraphic(type1[1]);
                break;
            case -1:
                this.setGraphic(type1[2]);
                break;
            case 0:
                this.setGraphic(type1[3]);
                break;
            case 1:
                this.setGraphic(type1[4]);
                break;
            case 2:
                this.setGraphic(type1[5]);
                break;
            case 3:
                this.setGraphic(type1[6]);
                break;
            default:
                break;
        }
    }

    /** Adds another ship to the chain in a formation.
     */
    public void addFollower() {
        this.follower = new Enemy(this.gameScreen, this, this.position + 1, this.formation, this.type);
        // If the formation is not yet full, adds another follower
        if (this.formation > (this.position + 1)) {
            this.follower.addFollower();
        }
    }

    /** Changes this ship's speed if a follower has changed direction.
     */
    private void followerChangingDir() {
        this.xspeed = follower.xspeed;
    }

    /** Changes this ship's speed if the leader has changed direction.
     */
    private void leaderChangingDir() {
        this.xspeed = leader.xspeed;
    }

    /** This method handles the changing of ranks if one ship has been destroyed.
     */
    private void positionChange() {
        this.position -= 1;
        if (follower != null) {
            follower.positionChange();
        }
    }
}
